package org.mihalovic.PES.GUI;

import java.beans.PropertyChangeEvent;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.TreeSet;
import java.beans.PropertyChangeListener;

import edu.uci.ics.jung.graph.DirectedSparseGraph;

import org.mihalovic.PES.Core.Graph.GraphHolder;
import org.mihalovic.PES.Core.Graph.Edge;
import org.mihalovic.PES.Core.Graph.Node;
import org.mihalovic.PES.Core.Graph.RankBasedNodeComparator;
import org.mihalovic.PES.Core.Graph.Rank.PageRankEvaluator;
import org.mihalovic.PES.data.IQueryChangedListener;



/**
 *
 */
class TextRankEvaluatorComponent extends JPanel implements IQueryChangedListener {
    /** minimal and preferred width of this component */
    public static final int DEFAULT_WIDTH = 290;
    /** minimal and preferred height of this component */
    public static final int DEFAULT_HEIGHT = 500;

    private PageRankEvaluator ranker;

    private JPanel upperComponentsPanel;

    private JLabel queryLabel;
    private JFormattedTextField precisionField;
    private JFormattedTextField iterationsField;
    private JButton rankButton;
    private JButton stepButton;
    private JButton resetButton;
    private JLabel infoLabel;
    private JScrollPane scrollPane;
    private JTextArea ranksArea;
    
    private VisualGraphPanel graphPanel;


    /**
     * @param rankName title for evaluating method (graph ranking algorithm)
     * @param evaluator IGraphEvaluator instance to commit ranking
     */
    public TextRankEvaluatorComponent(PageRankEvaluator ranker) {
        this.ranker = ranker;

        JPanel leftPanel = new JPanel();        
        leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS));

        leftPanel.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
        leftPanel.setMaximumSize(new Dimension(DEFAULT_WIDTH, 10240));

        upperComponentsPanel = buildUpperComponentsPanel();
        leftPanel.add(upperComponentsPanel);

        ranksArea = new JTextArea();
        ranksArea.setForeground(Color.BLACK);
        ranksArea.setFont(new Font("Courier new", Font.PLAIN, 11));
        ranksArea.setEditable(false);

        scrollPane = new JScrollPane(ranksArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scrollPane.setMinimumSize(new Dimension(DEFAULT_WIDTH, (int)(0.5*DEFAULT_HEIGHT)));
        scrollPane.setPreferredSize(new Dimension(DEFAULT_WIDTH, (int)(0.5*DEFAULT_HEIGHT)));

        leftPanel.add(scrollPane);

        JPanel rightPanel = new JPanel();
        rightPanel.setBackground(Color.BLUE);
        rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
        
        graphPanel = new VisualGraphPanel();
        rightPanel.add(graphPanel);
        
        rightPanel.setMinimumSize(new Dimension(250, 420));

        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        add(leftPanel);
        add(rightPanel);
    }


    private JPanel buildUpperComponentsPanel() {
        JPanel ucp = new JPanel();

        DecimalFormat precisionFormat = new DecimalFormat("####.0000000000000000");
        precisionFormat.setMinimumFractionDigits(2);
        precisionFormat.setMinimumIntegerDigits(1);
        precisionField = new JFormattedTextField(precisionFormat);
        precisionField.addPropertyChangeListener("value", new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                Object source = evt.getSource();
                if (source == precisionField) {
                    ranker.setPrecision( ((Number)precisionField.getValue()).doubleValue() );
                }
            }
        });
        precisionField.setColumns(12);
        precisionField.setValue(ranker.getPrecision());

        java.text.NumberFormat iterationsFormat = java.text.NumberFormat.getIntegerInstance();
        iterationsFormat.setMaximumIntegerDigits(4);
        iterationsField = new JFormattedTextField(iterationsFormat);
        iterationsField.setColumns(4);
        iterationsField.setValue(ranker.getPrecision());
        iterationsField.addPropertyChangeListener("value", new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                Object source = evt.getSource();
                if (source == iterationsField) {
                    ranker.setMaxIterations( ((Number)iterationsField.getValue()).intValue() );
                }
            }
        });
        iterationsField.setValue(ranker.getMaxIterations());

        stepButton = new JButton("Step 1 iteration");
        stepButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                if(!ranker.isStepping()) {          //Go from beginning. TODO: I don't like this. When over, the STEP button should be disabled until RESET.
                    DirectedSparseGraph<Node, Edge> g = GraphHolder.GetUnrankedGraph();
                    if(g==null)
                        return;
                    ranker.SetGraph(g);
                    setInfoText(0);                    
                }
                
                graphPanel.Clear();
                ranker.Step();
                graphPanel.setGraph(ranker.getGraph(), ranker.getMaxRank());
                printRankResult(ranker.getGraph(), ranker.getIterations());
                ranksArea.moveCaretPosition(0);
            }
        });
        
        rankButton = new JButton("Rank to end");
        rankButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e)
            {
                DirectedSparseGraph<Node, Edge> g = GraphHolder.GetUnrankedGraph();
                if(g==null)
                    return;
                graphPanel.Clear();
                ranker.SetGraph(g);
                ranker.Evaluate();
                graphPanel.setGraph(g, ranker.getMaxRank());
                printRankResult(g, ranker.getIterations());
                ranksArea.moveCaretPosition(0);
            }
        });
        
        resetButton = new JButton("RESET");
        resetButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                reset();
            }
        });

        GridBagConstraints gbc = new GridBagConstraints();

        ucp.setLayout( new GridBagLayout() );

        gbc.gridy=0;
        gbc.gridx=0;
        gbc.anchor = GridBagConstraints.LINE_START;
        ucp.add(new JLabel("<html><h3>Query: </h3></html>"), gbc);
        queryLabel = new JLabel();
        clearQuery();
        gbc.gridx=1;
        ucp.add(queryLabel, gbc);
        
        gbc.gridy=1;
        gbc.gridx=0;
        gbc.insets = new Insets(0,0,10,0);
        gbc.anchor= GridBagConstraints.LAST_LINE_START;
        ucp.add(new JLabel("PageRank params: "), gbc );

        gbc.gridy=2;
        gbc.gridx=0;
        gbc.insets = new Insets(0,0,0,0);
        ucp.add(new JLabel("Precision: "), gbc);

        gbc.gridy=2;
        gbc.gridx=1;
        ucp.add( precisionField, gbc);

        gbc.gridy=3;
        gbc.gridx=0;
        ucp.add(new JLabel("Max. iterations: "), gbc);

        gbc.gridy=3;
        gbc.gridx=1;
        ucp.add(iterationsField, gbc);

        gbc.gridy=4;
        gbc.gridx=0;
        gbc.insets = new Insets(10, 0, 0, 5);
        ucp.add(stepButton, gbc);
        gbc.gridx=1;
        ucp.add(rankButton, gbc);
        gbc.gridx=2;
        ucp.add(resetButton, gbc);
        
        infoLabel = new JLabel();
        gbc.gridy=5;
        gbc.gridx=0;
        gbc.gridwidth=3;    //ColSpan
        gbc.insets = new Insets(20, 0, 0, 0);
        ucp.add(infoLabel, gbc);
        setInfoText(0);

        ucp.setPreferredSize(new Dimension(DEFAULT_WIDTH, 200));

        JPanel ucpHolder = new JPanel(new BorderLayout());
        ucpHolder.setMaximumSize(new Dimension(DEFAULT_WIDTH, 180));
        ucpHolder.setPreferredSize(new Dimension(DEFAULT_WIDTH, 180));

        ucpHolder.add(ucp, BorderLayout.WEST);
        return ucpHolder;
    }

    
    private void setQuery(String query) {
        if(query==null || query.isEmpty()) {
            clearQuery();
            return;
        }

        setInputsEnabled(true);
        String toolTip = "<html><h5>";
        String[] parts = query.split(" OR ");
        int count=parts.length;

        queryLabel.setForeground(Color.BLACK);
        queryLabel.setText("<html><u>" + count + " domains</u></html>");
        for(int i=0; i<count; i++) {
            toolTip += parts[i];
            if(i < count-1)
                toolTip += " OR ";
            if(i>0 && i%3==0)
                toolTip += "<br/>";
        }
        
        queryLabel.setToolTipText(toolTip + "</h5></html>");
    }


    private void clearQuery() {
        queryLabel.setText("No search query");
        queryLabel.setForeground(Color.RED);
        queryLabel.setToolTipText("Go to Data tab to make a query");
        setInputsEnabled(false);
    }
    
    private void setInputsEnabled(boolean enabled) {
        precisionField.setEnabled(enabled);
        iterationsField.setEnabled(enabled);
        stepButton.setEnabled(enabled);
        rankButton.setEnabled(enabled);
        resetButton.setEnabled(enabled);
    }

    private void setInfoText(int iterations) {
        infoLabel.setText("PageRank score after " + iterations + " iterations:");
    }

    private void resetRanker() {
        DirectedSparseGraph<Node, Edge> g = GraphHolder.GetUnrankedGraph();
        ranker.reinitializeEvaluator(g);
    }


    private void printRankResult(DirectedSparseGraph<Node, Edge> graph, int iterations) {
        ranksArea.setText("");
        ranksArea.append("#.) NICK    :\t\t\tRANK\n\n");

        int rank = 1;

        Collection<Node> nodes = graph.getVertices();

        TreeSet<Node> tset = new TreeSet<Node>(new RankBasedNodeComparator());
        tset.addAll(nodes);

/*DEBUG        try{                 //Needs import java.io.*;
            File debugFile = new File("B:\\ratings.txt");
            Writer debugWriter = new BufferedWriter(new FileWriter(debugFile));
            debugWriter.write("User ID, User name, PageRank \n"); */

            for( Node temp = tset.pollLast(); temp != null; temp=tset.pollLast())
            {
                if(!temp.HasInEdges())          //Not interested in un-agreed members
                    break;

                String rankString = rank + ".) " + temp.getName() + " :";
                int missing = 30 - rankString.length();
                for(int i=0; i<missing; i++)
                    rankString += " ";

                ranksArea.append(rankString + new DecimalFormat("#.#####").format(temp.getRank().DoubleValue()) + "\n");

                rank++;

//                String debugString = temp.getID() + ", " + temp.getName() + ", " + temp.getRank().toString() + "\n";
//                debugWriter.write(debugString);
                if(rank>1000)
                {
                    ranksArea.append("\n\n And more...");
                    break;
                }
            }
            setInfoText(iterations);
/*
            debugWriter.close();
        }
        catch(IOException ioe)
        {
            JOptionPane.showMessageDialog(this, ioe.getMessage());
        } */
    }
    
    
    private void reset(){
        resetRanker();
        setInfoText(0);
        ranksArea.setText("");
        graphPanel.Clear();
    }

    public void QueryChanged(String newUserQuery) {
        setQuery(newUserQuery);
        setInfoText(0);
        ranksArea.setText("");
        graphPanel.Clear();
    }
}
